package com.senyint.sys.utils.net.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.senyint.sys.utils.POJOUtil;
import com.senyint.sys.utils.io.IOUtil;
import com.senyint.sys.utils.json.JSONUtil;
import com.senyint.sys.utils.log.Log;

/**
 * http请求工具类
 * 
 * @author NaiFei Wang
 * @version 1.0
 * @author NaiFei Wang
 * @version 1.1 post方法时 url参数放到query string中
 */
public class RequestUtil {

	public static final String METHOD_POST = "POST";
	public static final String METHOD_GET = "GET";
	public static final String METHOD_HEAD = "HEAD";
	public static final String METHOD_OPTIONS = "OPTIONS";
	public static final String METHOD_PUT = "PUT";
	public static final String METHOD_DELETE = "DELETE";
	public static final String METHOD_TRACE = "TRACE";

	public RequestOptions requestOptions;

	public int responseCode = 0;

	public boolean connected = false;

	public RequestUtil(RequestOptions requestOptions) {
		this.requestOptions = requestOptions;
	}

	/**
	 * @param requestOptions
	 * @return InputStream
	 * @throws IOException
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
	public InputStream get() throws IOException {
		String httpUrl = requestOptions.httpUrl;
		String method = requestOptions.method;
		if (METHOD_GET.equals(method) && requestOptions.data != null) {
			httpUrl = httpUrl + (httpUrl.indexOf("?") > -1 ? "&" : "?") + toQueryString(requestOptions.data);
		}
		if (requestOptions.isHttpsUrl) {
			// -Djavax.net.ssl. trustStore= C:\asf\asfSystemTest.ks
			// -Djavax.net.ssl. trustStore Password=changeit
			if (requestOptions.trustCertificates) {
				try {
					SSLContext sc = SSLContext.getInstance("SSL");
					sc.init(null, new TrustManager[] { new miTM() }, null);
					HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
				} catch (NoSuchAlgorithmException e) {
					e.printStackTrace();
				} catch (KeyManagementException e) {
					e.printStackTrace();
				}

				HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
					public boolean verify(String urlHostName, SSLSession session) {
						Log.INSTANCE.debug("Warning: URL Host: {} vs. {}", urlHostName, session.getPeerHost());
						return true;
					}
				});
			}
		}

		Log.INSTANCE.debug("请求地址：{}", httpUrl);
		URL url = new URL(httpUrl);

		Proxy proxy = requestOptions.proxy;

		HttpURLConnection httpConn = null;
		if (proxy != null) {
			httpConn = (HttpURLConnection) url.openConnection(proxy);
		} else {
			httpConn = (HttpURLConnection) url.openConnection();
		}

		// httpConn.setRequestProperty("accept", "*/*");
		// httpConn.setRequestProperty("connection", "Keep-Alive");
		// httpConn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
		// httpConn.setRequestProperty("content-type", "application/x-www-form-urlencoded;charset=utf-8");
		// "Proxy-Authorization"= "Basic Base64.encode(user:password)"
		// String headerKey = "Proxy-Authorization";
		// String headerValue = "Basic " + Base64.encode((proxyuser + ":" + proxypassword).getBytes());
		// httpConn.setRequestProperty(headerKey, headerValue);
		for (String key : requestOptions.requestProperty.keySet()) {
			httpConn.setRequestProperty(key, requestOptions.requestProperty.get(key));
		}
		httpConn.setConnectTimeout(requestOptions.connectTimeout * 1000);
		httpConn.setReadTimeout(requestOptions.readTimeout * 1000);
		Log.INSTANCE.debug("发送{}请求", method);
		httpConn.setRequestMethod(method);
		httpConn.setUseCaches(requestOptions.usecaches);

		httpConn.setDoInput(true);
		if (METHOD_POST.equals(method) && requestOptions.data != null) {
			httpConn.setDoOutput(true);
			try (OutputStream outputStream = httpConn.getOutputStream();) {
				write(outputStream, requestOptions.data);
			} catch (Exception e) {
				Log.INSTANCE.error("请求异常", e);
			}
		}
		responseCode = httpConn.getResponseCode();
		Log.INSTANCE.debug("请求返回：ResponseCode:{}", responseCode);
		if (responseCode == 400 || responseCode == 200) {
			connected = true;
			return httpConn.getInputStream();
		} else {
			try (InputStream inputStream = httpConn.getInputStream();) {
				Log.INSTANCE.warn("请求成功 但是状态是{},不是400 或者200 ！！！！！！！！！！！！！！！！！！！！！！！！！", responseCode);
				return inputStream;
			} catch (IOException e) {
				InputStream errorStream = httpConn.getErrorStream();
				Log.INSTANCE.error("请求失败,服务器返回：{}", IOUtil.inputStreamToString(errorStream, "utf-8"));
				throw new IOException(e);
			}
		}
	}

	/**
	 * @Title: write
	 * @Description: TODO
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年6月12日 下午5:42:59
	 * @version 1.0
	 * @param out
	 * @param data
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	private void write(OutputStream out, Object data) throws IOException {
		if (data instanceof String) {
			Log.INSTANCE.debug("请求参数：{}", data);
			out.write(((String) data).getBytes());
		} else if (data instanceof Map) {
			String param = mapToQueryString((Map<String, Object>) data);
			Log.INSTANCE.debug("请求参数：{}", param);
			out.write(param.getBytes());
		} else if (data instanceof InputStream) {
			Log.INSTANCE.debug("请求参数：{}", data);
			IOUtil.outputTrad((InputStream) data, out, 0, 0, 0);
		} else {
			String param = mapToQueryString(JSONUtil.jsonToObject(JSONUtil.jsonFromObject(data), Map.class));
			Log.INSTANCE.debug("请求参数：{}", param);
			out.write(param.getBytes());
		}
	}

	/**
	 * @param data
	 * @return
	 * @throws JsonParseException
	 * @throws JsonMappingException
	 * @throws JsonGenerationException
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	public static String toQueryString(Object data) throws JsonParseException, JsonMappingException, JsonGenerationException, IOException {
		String param = "";
		if (data instanceof String) {
			param = (String) data;
		} else if (data instanceof Map) {
			param = mapToQueryString((Map<String, Object>) data);
		} else {
			param = mapToQueryString(JSONUtil.jsonToObject(JSONUtil.jsonFromObject(data), Map.class));
		}
		return param;
	}

	/**
	 * @return String
	 * @throws IOException
	 */
	public String getString(String charset) throws IOException {
		InputStream inputStream = this.get();
		return IOUtil.inputStreamToString(inputStream, charset);
	}

	/**
	 * @return String
	 * @throws IOException
	 */
	public String getString() throws IOException {
		return this.getString("UTF-8");
	}

	/**
	 * @param data
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String mapToQueryString(Map<String, Object> data) throws UnsupportedEncodingException {
		StringBuffer raturn = new StringBuffer();
		if (data != null) {
			Set<String> keySet = data.keySet();
			int i = 0;
			for (String key : keySet) {
				if (i != 0) {
					raturn.append("&");
				}
				raturn.append(key).append("=").append(paramEncode(POJOUtil.convert(data.get(key), String.class)));
				i++;
			}
		}
		return raturn.toString();
	}

	/**
	 * @param data
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String mapToQueryStringForGet(Map<String, Object> data) throws UnsupportedEncodingException {
		StringBuffer raturn = new StringBuffer();
		if (data != null) {
			Set<String> keySet = data.keySet();
			int i = 0;
			for (String key : keySet) {
				if (i != 0) {
					raturn.append("&");
				}
				raturn.append(key).append("=").append(paramEncode(paramEncode(POJOUtil.convert(data.get(key), String.class))));
				i++;
			}
		}
		return raturn.toString();
	}

	/**
	 * @param data
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static Map<String, Object> queryStringToMap(String qryStr) {
		Map<String, Object> raturn = new HashMap<String, Object>();
		String[] params = qryStr.split("&");
		for (String param : params) {
			String[] split = param.split("=");
			raturn.put(split[0], split[1]);
		}
		return raturn;
	}

	/**
	 * @param params
	 * @param encoding
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String paramEncode(String params) throws UnsupportedEncodingException {
		if (params != null) {
			params = URLEncoder.encode(params, "utf-8");
		}
		return params;
	}

	/**
	 * @Title: getStreamAndPostStream
	 * @Description: 从源URL获取流post到目标URL
	 * @author WangNaiFei,WangNaiFei@senyint.com
	 * @date 2018年3月23日 上午10:03:49
	 * @version 1.0
	 * @param geturlstr
	 * @param posturlstr
	 * @param byteLenght
	 * @return
	 * @throws IOException
	 */
	public static boolean getStreamAndPostStream(String geturlstr, String posturlstr, int byteLenght) throws IOException {
		URLConnection getConnection = new URL(geturlstr).openConnection();
		InputStream inputStream = getConnection.getInputStream();

		URL posturl = new URL(posturlstr);
		HttpURLConnection postconn = (HttpURLConnection) posturl.openConnection();
		postconn.setReadTimeout(10 * 1000);
		postconn.setConnectTimeout(1 * 1000);
		postconn.setRequestMethod("POST");
		postconn.setUseCaches(false);
		postconn.setDoOutput(true);
		postconn.setDoInput(true);

		OutputStream outputStream = postconn.getOutputStream();
		byte[] b = new byte[byteLenght];
		int byteread = 0;
		while ((byteread = inputStream.read(b)) != -1) {
			outputStream.write(b, 0, byteread);
		}
		inputStream.close();
		outputStream.flush();
		outputStream.close();
		int responseCode = postconn.getResponseCode();
		boolean raturn = false;
		if (responseCode == 400 || responseCode == 200) {
			raturn = true;
		}
		return raturn;
	}

	public static void main(String args[]) throws IOException {
		// System.out.println(String.valueOf(null));
		String param = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><message><sex>男</sex><noon>2</noon><patientFolk>01</patientFolk><patientName>冉雨</patientName><deviceName>6飞利浦DR</deviceName><studyId>DR150814021</studyId><priority>3</priority><age>20</age><studyDescribe>腹部&amp;立位</studyDescribe><queueID>22</queueID><studyTime>2015-08-14 20:32:00</studyTime><patientOutPatientID>0000234834</patientOutPatientID><queueNumber>48</queueNumber><expectTime>2015-08-14 20:32:00</expectTime><responseCode>10</responseCode><emergencyFlag>1</emergencyFlag><deviceTypeName>DR</deviceTypeName><patientID>0000234834</patientID><operator>auto</operator><patientEName>RAN YU</patientEName><patientTypeID>门诊</patientTypeID><xeguid>9B96FB7D60E544A28AB6486901748B4D</xeguid></message>";
		RequestUtil requestUtil = new RequestUtil(new RequestOptions().setHttpUrl("http://172.16.180.39/gateservice/Pacs/GetRequisition.json").setData(param));
		System.out.println(requestUtil.getString());
	}

	static class miTM implements TrustManager, X509TrustManager {
		public java.security.cert.X509Certificate[] getAcceptedIssuers() {
			return null;
		}

		public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
			return true;
		}

		public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
			return true;
		}

		public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
			return;
		}

		public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
			return;
		}
	}

}